﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Dapper.Moon
{
    internal partial class DapperMoon : IDapperMoon
    {
        private IDatabase _Database = null;
        private string _connectionString = null;
        private DbType _dbType = DbType.SqlServer;

        private InsertableBatchImpl insertableBatch = null;
        private InsertableBulkBatchImpl insertableBulkBatch = null;
        private UpdateableBatchImpl updateableBatch = null;
        private DeleteableBatchImpl deleteableBatch = null;
        private IQueryableUnion queryableUnion = null;

        public DapperMoon(string connectionString, DbType dbType = DbType.SqlServer)
        {
            this._connectionString = connectionString;
            this._dbType = dbType;
        }

        /// <summary>
        /// 执行前
        /// </summary>
        public Action<ExecutingArgs> OnExecuting { get; set; }
        /// <summary>
        /// 执行后
        /// </summary>
        public Action<ExecutedArgs> OnExecuted { get; set; }

        /// <summary>
        /// 数据库通用操作方法，由Dapper实现
        /// </summary>
        public IDatabase Database
        {
            get
            {
                if (_Database == null)
                {
                    _Database = InstanceFactory.CreateInstance<IDatabase>(_dbType.ToString() + "Database", new object[] { _connectionString });
                    _Database.Aop = new AopProvider();
                    _Database.Aop.OnExecuting = OnExecuting;
                    _Database.Aop.OnExecuted = OnExecuted;
                }
                return _Database;
            }
        }

        #region Transaction
        /// <summary>
        /// 提供给跨库事务的方法 可直接用 RunTransaction
        /// </summary>
        /// <param name="isolationLevel"></param>
        public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            Database.BeginTransaction(isolationLevel);
        }
        /// <summary>
        /// 提供给跨库事务的方法 可直接用 RunTransaction
        /// </summary>
        /// <param name="isolationLevel"></param>
        public void CommitTransaction()
        {
            Database.CommitTransaction();
        }
        /// <summary>
        /// 提供给跨库事务的方法 可直接用 RunTransaction
        /// </summary>
        /// <param name="isolationLevel"></param>
        public void RollbackTransaction()
        {
            Database.RollbackTransaction();
        }
        /// <summary>
        /// 事务
        /// </summary>
        /// <param name="action"></param>
        /// <param name="isolationLevel"></param>
        /// <returns></returns>
        public RunTransactionResult RunTransaction(Action action, IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            return Database.RunTransaction(action, isolationLevel);
        }
        #endregion Transaction

        private IInsertable<T> _Insertable<T>()
        {
            IInsertable<T> insertable = InstanceFactory.CreateInsertable<T>(_dbType, Database);
            return insertable;
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public IInsertable<T> Insertable<T>(T t)
        {
            IInsertable<T> insertable = _Insertable<T>();
            return insertable.Insertable(t);
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <returns></returns>
        public IInsertable<T> Insertable<T>(List<T> list)
        {
            IInsertable<T> insertable = _Insertable<T>();
            return insertable.Insertable(list);
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <returns></returns>
        public IInsertable<T> Insertable<T>()
        {
            IInsertable<T> insertable = _Insertable<T>();
            return insertable.Insertable(null);
        }

        private IUpdateable<T> _Updateable<T>()
        {
            IUpdateable<T> updateable = InstanceFactory.CreateUpdateable<T>(_dbType, Database);
            return updateable;
        }

        /// <summary>
        /// 修改
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public IUpdateable<T> Updateable<T>(T t)
        {
            IUpdateable<T> updateable = _Updateable<T>();
            return updateable.Updateable(t);
        }

        /// <summary>
        /// 修改
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IUpdateable<T> Updateable<T>()
        {
            IUpdateable<T> updateable = _Updateable<T>();
            return updateable.Updateable();
        }

        private IDeleteable<T> _Deleteable<T>()
        {
            IDeleteable<T> deleteable = InstanceFactory.CreateDeleteable<T>(_dbType, Database);
            return deleteable;
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IDeleteable<T> Deleteable<T>()
        {
            IDeleteable<T> deleteable = _Deleteable<T>();
            return deleteable.Deleteable();
        }

        /// <summary>
        /// 根据主键删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pks"></param>
        /// <returns></returns>
        public IDeleteable<T> Deleteable<T>(params object[] pks)
        {
            IDeleteable<T> deleteable = _Deleteable<T>();
            return deleteable.Deleteable(pks);
        }

        #region Queryable
        /// <summary>
        /// 查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IQueryable<T> Queryable<T>()
        {
            IQueryable<T> queryable = InstanceFactory.CreateQueryable<T>(_dbType, Database);
            return queryable;
        }

        /// <summary>
        /// 多表连接，表1 别名 t1,表2 别名 t2
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <returns></returns>
        public IQueryable<T, T2> Queryable<T, T2>()
        {
            IQueryable<T, T2> queryable = InstanceFactory.CreateQueryable<T, T2>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3> Queryable<T, T2, T3>()
        {
            IQueryable<T, T2, T3> queryable = InstanceFactory.CreateQueryable<T, T2, T3>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4> Queryable<T, T2, T3, T4>()
        {
            IQueryable<T, T2, T3, T4> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4, T5> Queryable<T, T2, T3, T4, T5>()
        {
            IQueryable<T, T2, T3, T4, T5> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4, T5>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4, T5, T6> Queryable<T, T2, T3, T4, T5, T6>()
        {
            IQueryable<T, T2, T3, T4, T5, T6> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4, T5, T6>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4, T5, T6, T7> Queryable<T, T2, T3, T4, T5, T6, T7>()
        {
            IQueryable<T, T2, T3, T4, T5, T6, T7> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4, T5, T6, T7>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4, T5, T6, T7, T8> Queryable<T, T2, T3, T4, T5, T6, T7, T8>()
        {
            IQueryable<T, T2, T3, T4, T5, T6, T7, T8> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4, T5, T6, T7, T8>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4, T5, T6, T7, T8, T9> Queryable<T, T2, T3, T4, T5, T6, T7, T8, T9>()
        {
            IQueryable<T, T2, T3, T4, T5, T6, T7, T8, T9> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4, T5, T6, T7, T8, T9>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4, T5, T6, T7, T8, T9, T10> Queryable<T, T2, T3, T4, T5, T6, T7, T8, T9, T10>()
        {
            IQueryable<T, T2, T3, T4, T5, T6, T7, T8, T9, T10> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_dbType, Database);
            return queryable;
        }

        public IQueryableUnion Union<T>(IQueryable<T> queryable)
        {
            if (queryableUnion == null)
            {
                queryableUnion = new QueryableUnionProvide(Database);
            }
            queryableUnion.Union(queryable);
            return queryableUnion;
        }

        public IQueryableUnion UnionAll<T>(IQueryable<T> queryable)
        {
            if (queryableUnion == null)
            {
                queryableUnion = new QueryableUnionProvide(Database);
            }
            queryableUnion.UnionAll(queryable);
            return queryableUnion;
        }
        #endregion Queryable

        public IDbFirst DbFirst()
        {
            var dbFirst = InstanceFactory.CreateInstance<IDbFirst>(_dbType.ToString() + "DbFirst", new object[] { Database });
            return dbFirst;
        }

        #region Batch
        public void InsertableBatch<T>(IInsertable<T> insertable)
        {
            if (insertableBatch == null)
            {
                insertableBatch = new InsertableBatchImpl();
            }
            insertableBatch.Attach(insertable);
        }

        public void InsertableBulkBatch<T>(IInsertable<T> insertable)
        {
            if (insertableBulkBatch == null)
            {
                insertableBulkBatch = new InsertableBulkBatchImpl();
            }
            insertableBulkBatch.Attach(insertable);
        }

        public void UpdateableBatch<T>(IUpdateable<T> updateable)
        {
            if (updateableBatch == null)
            {
                updateableBatch = new UpdateableBatchImpl();
            }
            updateableBatch.Attach(updateable);
        }

        public void DeleteableBatch<T>(IDeleteable<T> deleteable)
        {
            if (deleteableBatch == null)
            {
                deleteableBatch = new DeleteableBatchImpl();
            }
            deleteableBatch.Attach(deleteable);
        }

        public void BatchClear()
        {
            insertableBatch?.Clear();
            insertableBulkBatch?.Clear();
            updateableBatch?.Clear();
            deleteableBatch?.Clear();
        }

        public int ExecuteBatch()
        {
            int count = 0;
            count += insertableBatch == null ? 0 : insertableBatch.Execute();
            count += insertableBulkBatch == null ? 0 : insertableBulkBatch.Execute();
            count += updateableBatch == null ? 0 : updateableBatch.Execute();
            count += deleteableBatch == null ? 0 : deleteableBatch.Execute();
            BatchClear();
            return count;
        }

        public async Task<int> ExecuteBatchAsync()
        {
            int count = 0;
            count += insertableBatch == null ? 0 : await insertableBatch.ExecuteAsync();
            count += insertableBulkBatch == null ? 0 : await insertableBulkBatch.ExecuteAsync();
            count += updateableBatch == null ? 0 : await updateableBatch.ExecuteAsync();
            count += deleteableBatch == null ? 0 : await deleteableBatch.ExecuteAsync();
            BatchClear();
            return count;
        }
        #endregion Batch

        public void Dispose()
        {
            _Database?.Dispose();
        }
    }
}
